//=============================================================================
//
//		Copyright (C) 1995 by Paul S. McCarthy and Eric Sunshine.
//				Written by Paul S. McCarthy and Eric Sunshine.
//							All Rights Reserved.
//
//		This notice may not be removed from this source code.
//
//		This object is included in the MiscKit by permission from the authors
//		and its use is governed by the MiscKit license, found in the file
//		"License.rtf" in the MiscKit distribution.	Please refer to that file
//		for a list of all applicable permissions and restrictions.
//
//=============================================================================
//-----------------------------------------------------------------------------
// DirWindow.m
//
//		Manages window which displays directory listing.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id$
// $Log$
//-----------------------------------------------------------------------------
#import "DirWindow.h"
#import "NameCache.h"
#import "Defaults.h"
#import <misckit/MiscTableScroll.h>
#import <misckit/MiscTableCell.h>
#import <assert.h>
#import <errno.h>
#import <appkit/NXImage.h>
#import <appkit/workspaceRequest.h>
#import <sys/dir.h>

#ifdef _POSIX_SOURCE
# define GET_CURR_DIR(_BF_,_SZ_)		getcwd(_BF_,_SZ_)
#else
# define GET_CURR_DIR(_BF_,_SZ_)		getwd(_BF_)
#endif

#define USE_PRIVATE_ZONE		1

enum
	{
	ICON_SLOT,
	NAME_SLOT,
	SIZE_SLOT,
	MODIFIED_SLOT,
	PERMS_SLOT,
	OWNER_SLOT,
	GROUP_SLOT,
	HARDLINKS_SLOT,
	SOFTLINK_SLOT,
	MESSAGE_SLOT
	};
#define MAX_SLOT (MESSAGE_SLOT + 1)

#define CASCADE_MAX 10
static int cascadeCounter = 0;
static NXCoord cascadeOriginX;
static NXCoord cascadeOriginY;
static NXCoord cascadeDeltaX;
static NXCoord cascadeDeltaY;

static BOOL defaultAutoSort;
static BOOL defaultShowHidden;
static BOOL defaultHighlightDirs;
static NXColor defaultColor;
static NXSize defaultWinSize;
static Font* defaultFont;
static List* openDirs = 0;
static OwnerCache* owners = 0;
static GroupCache* groups = 0;

static char const COLOR_DEF[]			= "DirColor";
static char const SIZE_DEF[]			= "DirSize";
static char const FONT_DEF[]			= "DirFont";
static char const SORT_DEF[]			= "AutoSort";
static char const HIDDEN_DEF[]			= "ShowHidden";
static char const HLIGHT_DEF[]			= "HighlightDirs";
static char const COL_SIZES_DEF[]		= "ColSizes";
static char const COL_ORDER_DEF[]		= "ColOrder";


//-----------------------------------------------------------------------------
// normalize_path
//-----------------------------------------------------------------------------
static void normalize_path( char *buff, size_t sz )
	{
	char save_dir[ FILENAME_MAX + 1 ];
	char tmp[ FILENAME_MAX + 1 ];

	if (buff != 0 && sz > 0)
		{
		if (GET_CURR_DIR( save_dir, sizeof(save_dir) ) != 0)
			{
			if (chdir( buff ) == 0)
				{
				if (GET_CURR_DIR( tmp, sizeof(tmp) ) != 0)
					{
					int const tmp_len = strlen( tmp );
					if (tmp_len < sz)
						strcpy( buff, tmp );
					else
						{
						strncpy( buff, tmp, sz );
						buff[ sz - 1 ] = '\0';
						}
					}
				chdir( save_dir );
				}
			}
		}
	}


//-----------------------------------------------------------------------------
// fsize
//-----------------------------------------------------------------------------
static void fsize( size_t n, id cell )
	{
	[[cell setAlignment: NX_RIGHTALIGNED] setIntValue: n];
	}


//-----------------------------------------------------------------------------
// mdate
//-----------------------------------------------------------------------------
static void mdate( time_t t, id cell )
	{
	#define BUFF_SIZE 64
	char buff[ BUFF_SIZE ];
	strftime( buff, BUFF_SIZE, "%m/%d/%y %H:%M", localtime(&t) );
	[cell setStringValue:buff];
	[cell setTag: (int) t ];
	}


//-----------------------------------------------------------------------------
// perms
//-----------------------------------------------------------------------------
static void perms( unsigned int mode, id cell )
	{
	char buff[ 16 ];
	char* s;
	int ftype;
	int c;

	s = buff;

	ftype = (mode & S_IFMT);
	if (ftype == S_IFDIR)		c = 'd';
	else if (ftype == S_IFCHR)	c = 'c';
	else if (ftype == S_IFBLK)	c = 'b';
	else if (ftype == S_IFREG)	c = '-';
	else if (ftype == S_IFLNK)	c = 'l';
	else if (ftype == S_IFSOCK) c = 's';
	else c = '-';
	*s++ = c;
	*s++ = (mode & 0400) ? 'r' : '-';
	*s++ = (mode & 0200) ? 'w' : '-';
	*s++ = (mode & S_ISUID) ? 's' : (mode & 0100) ? 'x' : '-';
	*s++ = (mode & 0040) ? 'r' : '-';
	*s++ = (mode & 0020) ? 'w' : '-';
	*s++ = (mode & S_ISGID) ? 's' : (mode & 0010) ? 'x' : '-';
	*s++ = (mode & 0004) ? 'r' : '-';
	*s++ = (mode & 0002) ? 'w' : '-';
	*s++ = (mode & S_ISVTX) ? 't' : (mode & 0001) ? 'x' : '-';
	*s = '\0';

	[cell setStringValue: buff];
	[cell setTag: mode];
	}


//-----------------------------------------------------------------------------
// owner
//-----------------------------------------------------------------------------
static void owner( unsigned int uid, id cell )
	{
	[cell setStringValueNoCopy: [owners lookup:uid] ];
	}


//-----------------------------------------------------------------------------
// group
//-----------------------------------------------------------------------------
static void group( unsigned int gid, id cell )
	{
	[cell setStringValueNoCopy: [groups lookup:gid] ];
	}


//-----------------------------------------------------------------------------
// image
//-----------------------------------------------------------------------------
static void image( char const* name, id cell, NXCoord w, NXCoord h )
	{
	NXCoord s = (w < h ? w : h) - 1.0;
	NXSize sz = { s, s };
	NXImage* i = [[Application workspace] getIconForFile: name];
	[i setScalable: YES];
	[i setSize: &sz];
	[cell setImage: i];
	}



//=============================================================================
// IMPLEMENTATION
//=============================================================================
@implementation DirWindow

//-----------------------------------------------------------------------------
// + initCascader
//-----------------------------------------------------------------------------
+ (void) initCascader
	{
	NXSize screenSize;
	[NXApp getScreenSize: &screenSize];
	cascadeOriginX = screenSize.width / 4;
	cascadeOriginY = screenSize.height - 20;
	cascadeDeltaX = 20;
	cascadeDeltaY = 20;
	}


//-----------------------------------------------------------------------------
// + initialize
//-----------------------------------------------------------------------------
+ initialize
	{
	if (self == [DirWindow class])
		{
		[self initCascader];
		openDirs = [[List alloc] init];
		owners = [OwnerCache commonInstance];
		groups = [GroupCache commonInstance];
		defaultColor = [Defaults getColor:COLOR_DEF fallback:NX_COLORLTGRAY];
		defaultAutoSort = [Defaults getBool:SORT_DEF fallback:NO];
		defaultShowHidden = [Defaults getBool:HIDDEN_DEF fallback:NO];
		defaultHighlightDirs = [Defaults getBool:HLIGHT_DEF fallback:NO];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - cascade
//-----------------------------------------------------------------------------
- (void) cascade
	{
	NXCoord top,left;
	left = cascadeOriginX + (cascadeDeltaX * cascadeCounter);
	top	 = cascadeOriginY - (cascadeDeltaY * cascadeCounter);
	[window moveTopLeftTo: left : top ];
	if (++cascadeCounter >= CASCADE_MAX)
		cascadeCounter = 0;
	}


//-----------------------------------------------------------------------------
// - isDir:
//-----------------------------------------------------------------------------
- (BOOL) isDir:(int)r
	{
	unsigned int const m = (unsigned int) [[scroll cellAt:r:PERMS_SLOT] tag];
	return ((m & S_IFMT) == S_IFDIR);
	}


//-----------------------------------------------------------------------------
// - updateButtons
//-----------------------------------------------------------------------------
- (void) updateButtons
	{
	BOOL const enable = [scroll numSelectedRows] == 1 &&
						[self isDir:[scroll selectedRow]];
	if (enable != [cdButton isEnabled])
		[cdButton setEnabled: enable];
	}


//-----------------------------------------------------------------------------
// - setRow:useOwner:color:
//-----------------------------------------------------------------------------
- (void) setRow:(int)r useOwner:(BOOL)useOwner color:(NXColor)color
	{
	int i;
	for (i = MAX_SLOT; i-- >= 0; )
		{
		id cell = [scroll cellAt:r:i];
		if (useOwner &&
			[cell respondsTo:@selector(setUseOwnerBackgroundColor:)])
			{
			[cell setUseOwnerBackgroundColor:YES];
			if ([cell respondsTo:@selector(setOwnerBackgroundColor:)])
				[cell setOwnerBackgroundColor:color];
			}
		else
			{
			if ([cell respondsTo: @selector(setBackgroundColor:)])
				[cell setBackgroundColor: color];
			}
		}
	}


//-----------------------------------------------------------------------------
// - highlight:row:
//-----------------------------------------------------------------------------
- (void) highlight:(BOOL)flag row:(int)r
	{
	if (flag)
		[self setRow:r useOwner:NO color:NX_COLORCYAN];
	else
		[self setRow:r useOwner:YES color:defaultColor];
	}


//-----------------------------------------------------------------------------
// - highlightDirs:
//-----------------------------------------------------------------------------
- (void) highlightDirs:(BOOL)flag
	{
	int const N = [scroll numRows];
	int i;
	for (i = 0; i < N; i++)
		if ([self isDir:i])
			[self highlight:flag row:i];
	}


//-----------------------------------------------------------------------------
// - freeImages
//-----------------------------------------------------------------------------
- (void) freeImages
	{
	int i;
	int const n = [scroll numRows];
	for (i = 0; i < n; i++)		
		{
		id cell = [scroll cellAt:i:ICON_SLOT];
		id img	= [cell image];
		[cell setImage:0];
		[img free];
		}
	}


//-----------------------------------------------------------------------------
// - tableScroll:fontChangedFrom:to:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		fontChangedFrom:(Font*)oldFont
		to:(Font*)newFont
	{
	defaultFont = newFont;
	[Defaults set:FONT_DEF font:defaultFont];
	return self;
	}


//-----------------------------------------------------------------------------
// - tableScroll:border:slotResized:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		border:(MiscBorderType)b
		slotResized:(int)n
	{
	char* s;
	assert( b == MISC_COL_BORDER );
	s = [ts colSizesAsString:0 size:0 canExpand:YES];
	[Defaults set:COL_SIZES_DEF str:s];
	free( s );
	return self;
	}


//-----------------------------------------------------------------------------
// - tableScroll:border:slotDraggedFrom:to:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		border:(MiscBorderType)b
		slotDraggedFrom:(int)fromPos
		to:(int)toPos;
	{
	char* s;
	assert( b == MISC_COL_BORDER );
	s = [ts colOrderAsString:0 size:0 canExpand:YES];
	[Defaults set:COL_ORDER_DEF str:s];
	free( s );
	return self;
	}


//-----------------------------------------------------------------------------
// - fillScroll
//-----------------------------------------------------------------------------
- (void) fillScroll
	{
	size_t total_bytes = 0;
	int dirlen;
	char namebuff[ FILENAME_MAX + 1 ];
	DIR* dirp;

	dirlen = strlen( path );
	memcpy( namebuff, path, dirlen );

	if (dirlen == 0 || namebuff[ dirlen - 1 ] != '/')
		namebuff[ dirlen++ ] = '/';

	[window disableDisplay];

	[self freeImages];
	[scroll empty];

	if ((dirp = opendir( path )) == 0)
		{
		NXRunAlertPanel( "Can't Open",
				"Cannot open directory, %s\n%d:%s", "OK",0,0,
				path, errno, strerror(errno) );
		}
	else
		{
		int rc;
		int row = -1;
		struct stat st;
		struct direct const* dp;

		while ((dp = readdir(dirp)) != 0)
			{			// Do not include the "." (self) entry.
			int const dlen = dp->d_namlen;
			char const* const dname = dp->d_name;
			BOOL const dot_name = (dname[0] == '.');
			if ((dlen > 1 || !dot_name) &&		// exclude "."
				(showHidden || !dot_name ||
					(dlen == 2 && dname[1] == '.')))
				{
				[scroll addRow]; row++;
				[[scroll cellAt:row:NAME_SLOT] setStringValue:dname];
				strcpy( namebuff + dirlen, dname );
				if ((rc = lstat( namebuff, &st )) == 0)
					{
					if ((st.st_mode & S_IFMT) == S_IFLNK)		// soft-link?
						{
						char symbuf[ FILENAME_MAX + 1 ];
						int cc;
						cc = readlink( namebuff, symbuf, FILENAME_MAX );
						if (cc >= 0)
							{
							symbuf[ cc ] = '\0';
							[[scroll cellAt:row:SOFTLINK_SLOT]
												setStringValue:symbuf];
							rc = stat( namebuff, &st );
							}
						else
							rc = -1;
						}
					}
				if (rc == 0)
					{
					total_bytes += st.st_size;
					image( namebuff, [scroll cellAt:row:ICON_SLOT],
								[scroll colSize:ICON_SLOT],
								[scroll uniformSizeRows] );
					fsize( st.st_size, [scroll cellAt:row:SIZE_SLOT] );
					mdate( st.st_mtime,[scroll cellAt:row:MODIFIED_SLOT] );
					perms( st.st_mode, [scroll cellAt:row:PERMS_SLOT] );
					owner( st.st_uid, [scroll cellAt:row:OWNER_SLOT] );
					group( st.st_gid, [scroll cellAt:row:GROUP_SLOT] );
					[[scroll cellAt:row:HARDLINKS_SLOT]
								setIntValue:st.st_nlink];
					[[scroll cellAt:row:MESSAGE_SLOT]
								setStringValueNoCopy:""];
					if (highlightDirs && (st.st_mode & S_IFMT) == S_IFDIR)
						[self highlight:YES row:row];
					}
				else // (rc != 0)
					{
					int slot;
					[[scroll cellAt:row:MESSAGE_SLOT]
								setStringValue: strerror(errno)];
					for (slot = SIZE_SLOT; slot < SOFTLINK_SLOT; slot++)
						[[scroll cellAt:row:slot] setStringValueNoCopy:"?"];
					}
				}
			}
		closedir(dirp);
		}
	if ([scroll autoSortRows])
		[scroll sortRows];
	[scroll sizeToCells];
	sprintf( namebuff, "%d files   %lu bytes", [scroll numRows], total_bytes );
	[countField setStringValue:namebuff];
	[self updateButtons];
	[[window reenableDisplay] display];
	}


//-----------------------------------------------------------------------------
// - setPath:
//-----------------------------------------------------------------------------
- (void) setPath:(char const*)dirname
	{
	NXZone* const z = [self zone];
	int dirlen;
	if (path != 0) NXZoneFree( z, path );
	if (dirname == 0) dirname = NXHomeDirectory();
	if (dirname == 0) dirname = "/";
	dirlen = strlen( dirname ) + 1;
	path = (char*) NXZoneMalloc( z, dirlen );
	memcpy( path, dirname, dirlen );
	[window setTitle: path];
	}


//-----------------------------------------------------------------------------
// - load:
//-----------------------------------------------------------------------------
- (void) load:(char const*)dirname
	{
	[self setPath:dirname];
	[self fillScroll];
	}


//-----------------------------------------------------------------------------
// - open:
//-----------------------------------------------------------------------------
- open:sender
	{
	if ([scroll hasRowSelection])
		{
		int path_len;
		char buff[ FILENAME_MAX + 1 ];
		int i;
		MiscIntList* list = [[MiscIntList allocFromZone:[self zone]] init];

		strcpy( buff, path );
		path_len = strlen( buff );
		if (path_len == 0 || buff[path_len - 1] != '/')
			{
			buff[ path_len++ ] = '/';
			buff[ path_len ] = '\0';
			}
			
		[scroll selectedRows: list];
		for (i = [list count]; i-- > 0; )
			{
			unsigned int mode;
			int row = [list intAt:i];

			strcpy( buff + path_len,
						[[scroll cellAt:row:NAME_SLOT] stringValue] );

			mode = (unsigned int)[[scroll cellAt:row:PERMS_SLOT] tag];
			if ((mode & S_IFMT) == S_IFDIR)
				[[self class] launchDir: buff];
			else
				[[Application workspace] openFile: buff];
			}
		[list free];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - destroy:
//-----------------------------------------------------------------------------
- destroy:sender
	{
	if ([scroll hasRowSelection])
		{
		size_t total_len = 0;
		char* file_list;
		int i;
		NXZone* const z = [self zone];
		MiscIntList* list = [[MiscIntList allocFromZone:z] init];

		[scroll selectedRows: list];

		for (i = [list count]; i-- > 0; )
			{
			int const row = [list intAt:i];
			char const* const s = [[scroll cellAt:row:NAME_SLOT] stringValue];
			if (s != 0)
				total_len += strlen( s ) + 1;
			}

		file_list = (char*) NXZoneMalloc( z, total_len + 1 );
		
		total_len = 0;
		for (i = [list count]; i-- > 0; )
			{
			int row = [list intAt:i];
			char const* const s = [[scroll cellAt:row:NAME_SLOT] stringValue];
			if (s != 0)
				{
				int const len = strlen( s );
				strcpy( file_list + total_len, s );
				total_len += len;
				file_list[ total_len++ ] = '\t';
				}
			}
		file_list[ total_len ] = '\0';

		[[Application workspace]
				performFileOperation: WSM_DESTROY_OPERATION
						source: path
						destination: ""
						files: file_list
						options: ""];

		NXZoneFree( z, file_list );
		[list free];
		[self fillScroll];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - updatePressed:
//-----------------------------------------------------------------------------
- updatePressed:sender
	{
	[self fillScroll];
	return self;
	}


//-----------------------------------------------------------------------------
// - cdPressed:
//-----------------------------------------------------------------------------
- cdPressed:sender
	{
	if ([scroll numSelectedRows] == 1)
		{
		MiscCoord_P const row = [scroll selectedRow];
		if ([self isDir:row])
			{
			char buff[ FILENAME_MAX * 2 + 1 ];
			strcat( strcat( strcpy( buff, path ), "/" ),
				[[scroll cellAt:row:NAME_SLOT] stringValue] );
			normalize_path( buff, sizeof(buff) );
			[self load:buff];
			}
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - autoSortClick:
//-----------------------------------------------------------------------------
- autoSortClick:sender
	{
	BOOL const switchState = [autoSortSwitch state];
	if (autoSort != switchState)
		{
		defaultAutoSort = autoSort = switchState;
		[Defaults set:SORT_DEF bool:defaultAutoSort];
		[scroll setAutoSortRows:switchState];
		if (switchState)
			[scroll sortRows];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - hiddenFilesClick:
//-----------------------------------------------------------------------------
- hiddenFilesClick:sender
	{
	BOOL const switchState = [hiddenFilesSwitch state];
	if (showHidden != switchState)
		{
		defaultShowHidden = showHidden = switchState;
		[Defaults set:HIDDEN_DEF bool:defaultShowHidden];
		[self fillScroll];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - highlightClick:
//-----------------------------------------------------------------------------
- highlightClick:sender
	{
	BOOL const switchState = [highlightSwitch state];
	if (highlightDirs != switchState)
		{
		defaultHighlightDirs = highlightDirs = switchState;
		[Defaults set:HLIGHT_DEF bool:defaultHighlightDirs];
		[self highlightDirs:highlightDirs];
		[scroll display];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - didClick:
//-----------------------------------------------------------------------------
- didClick:sender
	{
	[self updateButtons];
	return self;
	}


//-----------------------------------------------------------------------------
// - didDoubleClick:
//-----------------------------------------------------------------------------
- didDoubleClick:sender
	{
	[self open:sender];
	return self;
	}


//-----------------------------------------------------------------------------
// - free
//-----------------------------------------------------------------------------
- free
	{
#if(USE_PRIVATE_ZONE)
	NXZone* const z = [self zone];
#endif
	[window close];
	[window disableDisplay];
	[self freeImages];
	[window free];
	if (path != 0)
		free( path );
	[super free];
#if(USE_PRIVATE_ZONE)
	NXDestroyZone( z );
#endif
	return 0;
	}


//-----------------------------------------------------------------------------
// - makeKeyAndOrderFront:
//-----------------------------------------------------------------------------
- makeKeyAndOrderFront:sender
	{
	[window makeKeyAndOrderFront:sender];
	return self;
	}


//-----------------------------------------------------------------------------
// - windowWillClose:
//-----------------------------------------------------------------------------
- windowWillClose:sender
	{
	[openDirs removeObject:self];
	[NXApp delayedFree: self];
	return self;
	}


//-----------------------------------------------------------------------------
// - windowDidResize:
//-----------------------------------------------------------------------------
- windowDidResize:sender
	{
	NXRect r; [sender getFrame:&r];
	if (r.size.width  != defaultWinSize.width ||
		r.size.height != defaultWinSize.height)
		{
		defaultWinSize = r.size;
		[Defaults set:SIZE_DEF size:defaultWinSize];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - setDefaultColor
//-----------------------------------------------------------------------------
- (void) setDefaultColor: (NXColor)c
	{
	defaultColor = c;
	[Defaults set:COLOR_DEF color:c];
	}


//-----------------------------------------------------------------------------
// - setDaColors:
//-----------------------------------------------------------------------------
- (void) setDaColors: (NXColor)c
	{
	[window disableDisplay];
	[window setBackgroundColor:c];
	[scroll setColor:c];
	[window reenableDisplay];
	}


//-----------------------------------------------------------------------------
// - draggingEntered:
//-----------------------------------------------------------------------------
- (NXDragOperation) draggingEntered:(id<NXDraggingInfo>)sender
	{
	return ([sender draggingSourceOperationMask] & NX_DragOperationGeneric ?
				NX_DragOperationGeneric : NX_DragOperationNone);
	}


//-----------------------------------------------------------------------------
// - performDragOperation:
//-----------------------------------------------------------------------------
- (BOOL) performDragOperation:(id<NXDraggingInfo>)sender
	{
	[self setDefaultColor:
				NXReadColorFromPasteboard( [sender draggingPasteboard] )];
	[self setDaColors: defaultColor];
	[window display];
	return YES;
	}


//-----------------------------------------------------------------------------
// - initDefaults
//-----------------------------------------------------------------------------
- (void) initDefaults
	{
	static BOOL initialized = NO;
	if (!initialized)
		{
		NXRect r; [window getFrame:&r];
		defaultWinSize = r.size;

		defaultFont = [Defaults getFont:FONT_DEF fallback:[scroll font]];
	
		initialized = YES;
		}
	}


//-----------------------------------------------------------------------------
// - loadDefaults
//-----------------------------------------------------------------------------
- (void) loadDefaults
	{
	NXRect r;
	char const* s;

	[window getFrame:&r];
	r.size = [Defaults getSize:SIZE_DEF fallback:defaultWinSize];
	[window placeWindow:&r];

	autoSort = defaultAutoSort;
	showHidden = defaultShowHidden;
	highlightDirs = defaultHighlightDirs;

	[autoSortSwitch setState:autoSort];
	[hiddenFilesSwitch setState:showHidden];
	[highlightSwitch setState:highlightDirs];

	[scroll setAutoSortRows:autoSort];
	[scroll setFont:defaultFont];
	[self setDaColors: defaultColor];

	s = [Defaults getStr:COL_SIZES_DEF fallback:0];
	if (s)
		[scroll setColSizesFromString:s];

	s = [Defaults getStr:COL_ORDER_DEF fallback:0];
	if (s)
		[scroll setColOrderFromString:s];
	}


//-----------------------------------------------------------------------------
// - initDir:
//-----------------------------------------------------------------------------
- initDir:(char const*)dirname
	{
	char buff[ FILENAME_MAX + 1 ];
	NXZone* const z = [self zone];

	[super init];
	[[NXBundle bundleForClass:[self class]]
		getPath:buff forResource:[[self class] name] ofType:"nib"];
	[NXApp loadNibFile:buff owner:self withNames:NO fromZone:z];
	[window registerForDraggedTypes:&NXColorPboardType count:1];
	path = 0;
	[scroll setDoubleAction: @selector(didDoubleClick:)];
	[self initDefaults];
	[self loadDefaults];
	[self load:dirname];
	[openDirs addObject:self];
	[self cascade];
	[window makeKeyAndOrderFront:self];
	return self;
	}


//-----------------------------------------------------------------------------
// - init
//-----------------------------------------------------------------------------
- init
	{
	return [self initDir: NXHomeDirectory()];
	}


//-----------------------------------------------------------------------------
// - path
//-----------------------------------------------------------------------------
- (char const*) path
	{
	return path;
	}


//-----------------------------------------------------------------------------
// + findDir:
//-----------------------------------------------------------------------------
+ (DirWindow*) findDir:(char const*) normalized_path
	{
	DirWindow* p = 0;
	char const* s;
	unsigned int i;
	unsigned int const lim = [openDirs count];
	if (normalized_path != 0)
		{
		for (i = 0;	 i < lim;  i++)
			{
			p = (DirWindow*) [openDirs objectAt:i];
			s = [p path];
			if (s != 0 && strcmp( s, normalized_path ) == 0)
				return p;
			}
		}
	return 0;
	}


//-----------------------------------------------------------------------------
// + launchDir:
//-----------------------------------------------------------------------------
+ launchDir: (char const*) dirname
	{
	DirWindow* p = 0;
	char buff[ FILENAME_MAX + 1 ];
	if (dirname == 0) dirname = NXHomeDirectory();
	if (dirname == 0) dirname = "/";
	strncpy( buff, dirname, sizeof(buff) );
	buff[ sizeof(buff) - 1 ] = '\0';
	normalize_path( buff, sizeof(buff) );
	if ((p = [self findDir:buff]) != 0)
		[p makeKeyAndOrderFront:self];
	else
		{
#if(USE_PRIVATE_ZONE)
		NXZone* z = NXCreateZone( vm_page_size, vm_page_size, YES );
		p = [[self allocFromZone:z] initDir:buff];
#else
		p = [[self alloc] initDir:buff];
#endif
		}
	return p;
	}


@end
